<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>管线流动特效</title>
    <style>
        canvas { border: 1px solid #ccc; }
    </style>
</head>
<body>
    <canvas id="pipelineCanvas" width="800" height="400"></canvas>
    <script>
        const canvas = document.getElementById('pipelineCanvas');
        const ctx = canvas.getContext('2d');

        const pipelinePath = [
            { x: 50, y: 200 }, { x: 200, y: 200 }, { x: 300, y: 100 },
            { x: 400, y: 300 }, { x: 600, y: 300 }
        ];

        const particles = [];
        const particleCount = 20;
        const particleSpeed = 2;

        class Particle {
            constructor() {
                this.pos = Math.random();
                this.size = Math.random() * 5 + 2;
                this.color = `hsl(${Math.random() * 360}, 80%, 50%)`;
            }
            update() {
                this.pos += particleSpeed / 1000;
                if (this.pos > 1) this.pos = 0;
            }
            draw(ctx) {
                const point = getPointOnPath(this.pos);
                const gradient = ctx.createRadialGradient(point.x, point.y, 0, point.x, point.y, this.size);
                gradient.addColorStop(0, this.color);
                gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');

                ctx.beginPath();
                ctx.arc(point.x, point.y, this.size, 0, Math.PI * 2);
                ctx.fillStyle = gradient;
                ctx.shadowBlur = 10;
                ctx.shadowColor = this.color;
                ctx.fill();
                ctx.shadowBlur = 0;
            }
        }

        for (let i = 0; i < particleCount; i++) {
            particles.push(new Particle());
        }

        function getPointOnPath(t) {
            const totalLength = pipelinePath.length - 1;
            const segment = t * totalLength;
            const segmentIndex = Math.floor(segment);
            const segmentFraction = segment - segmentIndex;

            if (segmentIndex >= totalLength) return pipelinePath[pipelinePath.length - 1];
            const start = pipelinePath[segmentIndex];
            const end = pipelinePath[segmentIndex + 1];
            return {
                x: start.x + (end.x - start.x) * segmentFraction,
                y: start.y + (end.y - start.y) * segmentFraction
            };
        }

        function drawPipeline() {
            ctx.beginPath();
            ctx.moveTo(pipelinePath[0].x, pipelinePath[0].y);
            for (let i = 1; i < pipelinePath.length; i++) {
                ctx.lineTo(pipelinePath[i].x, pipelinePath[i].y);
            }
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 10;
            ctx.stroke();
        }

        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawPipeline();
            particles.forEach(particle => {
                particle.update();
                particle.draw(ctx);
            });
            requestAnimationFrame(animate);
        }

        animate();
    </script>
</body>
</html>